<template>
  <div class="birth">
    <el-row style="height: 40px" v-if="showTitle">
      <el-col class="birth-time" :span="8"> 年</el-col>
      <el-col class="birth-time" :span="8">月</el-col>
      <el-col class="birth-time" :span="8">日</el-col>
    </el-row>
    <el-row class="birth-container disableSelect" :class="{ 'is-disabled': disabled }">
      <el-col class="birth-time" :span="8">
        <div class="scrollbar">
          <div ref="yearRef" class="scrollbar-wrap">
            <ul class="scrollbar-view">
              <li
                :class="{
                  'birth-time__item': true,
                  'is-active': birth.year === item,
                  'is-link': birth.year !== item,
                }"
                v-for="(item, index) in years"
                :key="item"
                @click="selectTime('year', item, index)"
              >
                {{ item }}
              </li>
            </ul>
          </div>
        </div></el-col
      >
      <el-col class="birth-time" :span="8"
        ><div class="scrollbar">
          <div ref="monthRef" class="scrollbar-wrap">
            <ul class="scrollbar-view">
              <li
                :class="{
                  'birth-time__item': true,
                  'is-active': birth.month === item,
                  'is-link': birth.month !== item,
                }"
                v-for="(item, index) in months"
                :key="item"
                @click="selectTime('month', item, index)"
              >
                {{ item }}
              </li>
            </ul>
          </div>
        </div></el-col
      >
      <el-col class="birth-time" :span="8"
        ><div class="scrollbar">
          <div ref="dateRef" class="scrollbar-wrap">
            <ul class="scrollbar-view">
              <li
                :class="{
                  'birth-time__item': true,
                  'is-active': birth.date === item,
                  'is-link': birth.date !== item,
                }"
                v-for="(item, index) in dates"
                :key="item"
                @click="selectTime('date', item, index)"
              >
                {{ item }}
              </li>
            </ul>
          </div>
        </div></el-col
      >
      <div v-if="disabled" class="select-disabled"></div>
    </el-row>
  </div>
</template>

<script lang="ts" setup>
import { ref, reactive, onBeforeMount, onBeforeUnmount, watch, nextTick } from "vue";

const props = withDefaults(
  defineProps<{
    birthday: Date;
    disabled?: boolean;
    // 是否显示标题
    showTitle?: boolean;
  }>(),
  {
    disabled: false,
    showTitle: true,
  }
);

const emits = defineEmits<{
  (e: "update:birthday", birthday: Date): void;
}>();

/**
 * 供用户选择的所有年份
 */
const years = reactive<Array<number>>([]);
/**
 * 供用户选择的所有年份
 */
const months = reactive<Array<number>>([]);
/**
 * 供用户选择的所有年份
 */
const dates = reactive<Array<number>>([]);

/**
 * 给年份栏元素一个ref属性
 */
const yearRef = ref<HTMLDivElement>();
/**
 * 给月份栏元素一个ref属性
 */
const monthRef = ref<HTMLDivElement>();
/**
 * 给日子栏元素一个ref属性
 */
const dateRef = ref<HTMLDivElement>();

/**
 * 设置供用户选择的所有日子(对应下的年份以及月份的日子)
 */
const setDate = (year: number, month: number): void => {
  dates.splice(0);
  switch (month) {
    case 1:
    case 3:
    case 5:
    case 7:
    case 8:
    case 10:
    case 12:
      for (let i = 1; i < 32; i++) dates.push(i);
      break;
    case 4:
    case 6:
    case 9:
    case 11:
      for (let i = 1; i < 31; i++) dates.push(i);
      break;
    case 2:
      if (year % 4 === 0 || (year % 100 === 0 && year % 400 === 0)) {
        for (let i = 1; i < 30; i++) dates.push(i);
        break;
      } else {
        for (let i = 1; i < 29; i++) dates.push(i);
        break;
      }
  }
};

/**
 * 用户所选择生日的年月日
 */
const birth = ref<{
  year: number;
  month: number;
  date: number;
}>({
  year: new Date().getFullYear(),
  month: new Date().getMonth() + 1,
  date: new Date().getDate(),
});

watch(
  () => props.birthday,
  (newValue) => {
    const timestamp: number = new Date(
      birth.value.year,
      birth.value.month - 1,
      birth.value.date
    ).getTime();
    const year: number = newValue.getFullYear();
    if (
      newValue.getTime() !== timestamp &&
      year >= 1970 &&
      year <= new Date().getFullYear()
    ) {
      removeScrollEvent();
      birth.value.year = year;
      birth.value.month = newValue.getMonth() + 1;
      birth.value.date = newValue.getDate();
      setDate(birth.value.year, birth.value.month);
      const yearIndex: number = years.findIndex((item) => item === birth.value.year);
      setTimeout(() => {
        yearRef.value!.scrollTop = 40 * yearIndex;
        monthRef.value!.scrollTop = 40 * (birth.value.month - 1);
        dateRef.value!.scrollTop = 40 * (birth.value.date - 1);
        setTimeout(() => addScrollEvent(), 50);
      });
    }
  }
);

/**
 * 年份栏开始滚动
 */
const yearScroll = (e: Event) => scroll("year", e);
/**
 * 月份栏开始滚动
 */
const monthScroll = (e: Event) => scroll("month", e);
/**
 * 日期栏开始滚动
 */
const dateScroll = (e: Event) => scroll("date", e);

/**
 * 给年份、月份、日期栏添加滚动事件
 */
const addScrollEvent = () => {
  yearRef.value?.addEventListener("scroll", yearScroll);
  monthRef.value?.addEventListener("scroll", monthScroll);
  dateRef.value?.addEventListener("scroll", dateScroll);
};
/**
 * 给年份、月份、日期栏移除滚动事件
 */
const removeScrollEvent = () => {
  yearRef.value?.removeEventListener("scroll", yearScroll);
  monthRef.value?.removeEventListener("scroll", monthScroll);
  dateRef.value?.removeEventListener("scroll", dateScroll);
};

// 年份栏、月份栏、日子栏滚动时触发的动作
const scroll = (() => {
  let timingYear: number = 0,
    timingMonth: number = 0,
    timingDate: number = 0;
  return (type: "year" | "month" | "date", e: Event) => {
    const ele: HTMLDivElement = e!.target as HTMLDivElement;
    const top: number = ele.scrollTop;
    // 该年份对应years下的索引值
    const count: number = Math.floor(top / 40);
    switch (type) {
      case "year":
        if (timingYear) clearTimeout(timingYear);
        birth.value.year = years[count];
        timingYear = window.setTimeout(() => {
          yearRef.value?.removeEventListener("scroll", yearScroll);
          const yearIndex: number = years.findIndex((item) => item === birth.value.year);
          yearRef.value!.scrollTop = 40 * yearIndex;
          setTimeout(() => yearRef.value?.addEventListener("scroll", yearScroll));
          if (birth.value.month !== 1) {
            monthRef.value?.removeEventListener("scroll", monthScroll);
            birth.value.month = 1;
            monthRef.value!.scrollTop = 0;
            setTimeout(() => monthRef.value?.addEventListener("scroll", monthScroll));
          }
          if (birth.value.date !== 1) {
            dateRef.value?.removeEventListener("scroll", dateScroll);
            birth.value.date = 1;
            dateRef.value!.scrollTop = 0;
            setTimeout(() => dateRef.value?.addEventListener("scroll", dateScroll));
          }
          // 重新调整月份和日期
          setDate(birth.value.year, birth.value.month);
        }, 200);
        break;
      case "month":
        if (timingMonth) clearTimeout(timingMonth);
        birth.value.month = months[count];
        timingMonth = window.setTimeout(() => {
          monthRef.value?.removeEventListener("scroll", monthScroll);
          monthRef.value!.scrollTop = 40 * (birth.value.month - 1);
          // 重新调整日期
          setDate(birth.value.year, birth.value.month);
          setTimeout(() => monthRef.value?.addEventListener("scroll", monthScroll));
          if (birth.value.date !== 1) {
            dateRef.value?.removeEventListener("scroll", dateScroll);
            birth.value.date = 1;
            dateRef.value!.scrollTop = 0;
            setTimeout(() => dateRef.value?.addEventListener("scroll", dateScroll), 0);
          }
        }, 200);
        break;
      case "date":
        if (timingDate) clearTimeout(timingDate);
        birth.value.date = dates[count];
        timingDate = window.setTimeout(() => {
          dateRef.value?.removeEventListener("scroll", dateScroll);
          dateRef.value!.scrollTop = 40 * (birth.value.date - 1);
          setTimeout(() => dateRef.value?.addEventListener("scroll", dateScroll));
        }, 200);
        break;
    }
    emits(
      "update:birthday",
      new Date(birth.value.year, birth.value.month - 1, birth.value.date)
    );
  };
})();

/**
 * 用户通过点击进行年月日的选择
 * @param type - 用户点击的时间类型
 * @param time - 用户所选择时间的值
 * @param index - 该值对应数组下的索引值
 */
const selectTime = (
  type: "year" | "month" | "date",
  time: number,
  index: number
): void => {
  switch (type) {
    case "year":
      if (time !== birth.value.year) {
        yearRef.value?.removeEventListener("scroll", yearScroll);
        birth.value.year = time;
        emits(
          "update:birthday",
          new Date(birth.value.year, birth.value.month - 1, birth.value.date)
        );
        yearRef.value!.scrollTop = 40 * index;
        setTimeout(() => {
          yearRef.value?.addEventListener("scroll", yearScroll);
          if (birth.value.month !== 1) {
            monthRef.value?.removeEventListener("scroll", monthScroll);
            birth.value.month = 1;
            monthRef.value!.scrollTop = 0;
            setTimeout(() => monthRef.value?.addEventListener("scroll", monthScroll));
          }
          if (birth.value.date !== 1) {
            dateRef.value?.removeEventListener("scroll", dateScroll);
            birth.value.date = 1;
            dateRef.value!.scrollTop = 0;
            setTimeout(() => dateRef.value?.addEventListener("scroll", dateScroll));
          }
          // 重新调整月份和日期
          setDate(birth.value.year, birth.value.month);
        });
      }
      break;
    case "month":
      if (time !== birth.value.month) {
        monthRef.value?.removeEventListener("scroll", monthScroll);
        birth.value.month = time;
        emits(
          "update:birthday",
          new Date(birth.value.year, birth.value.month - 1, birth.value.date)
        );
        monthRef.value!.scrollTop = 40 * index;
        setTimeout(() => {
          monthRef.value?.addEventListener("scroll", monthScroll);
          // 重新调整日期
          setDate(birth.value.year, birth.value.month);
          if (birth.value.date !== 1) {
            dateRef.value?.removeEventListener("scroll", dateScroll);
            birth.value.date = 1;
            dateRef.value!.scrollTop = 0;
            setTimeout(() => dateRef.value?.addEventListener("scroll", dateScroll));
          }
        });
      }
      break;
    case "date":
      if (time !== birth.value.date) {
        dateRef.value?.removeEventListener("scroll", dateScroll);
        birth.value.date = time;
        emits(
          "update:birthday",
          new Date(birth.value.year, birth.value.month - 1, birth.value.date)
        );
        dateRef.value!.scrollTop = 40 * index;
        setTimeout(() => dateRef.value?.addEventListener("scroll", dateScroll));
      }
      break;
  }
};

onBeforeMount(() => {
  const date: Date = new Date();
  const thisYear: number = date.getFullYear();
  // 设置所有年份
  for (let i = 1970; i <= thisYear; i++) years.unshift(i);
  // 设置所有月份
  for (let i = 1; i < 13; i++) months.push(i);
  const year: number = props.birthday.getFullYear();
  if (year >= 1970 && year <= thisYear) {
    birth.value.year = props.birthday.getFullYear();
    birth.value.month = props.birthday.getMonth() + 1;
    birth.value.date = props.birthday.getDate();
  }
  setDate(birth.value.year, birth.value.month);
  const yearIndex: number = years.findIndex((item) => item === birth.value.year);
  nextTick(() => {
    yearRef.value!.scrollTop = 40 * yearIndex;
    monthRef.value!.scrollTop = 40 * (birth.value.month - 1);
    dateRef.value!.scrollTop = 40 * (birth.value.date - 1);
    addScrollEvent();
  });
});

onBeforeUnmount(() => removeScrollEvent());
</script>

<style lang="scss" scoped>
.birth {
  margin: 10px 0 25px;
  height: 240px;
  box-sizing: border-box;

  &-time {
    text-align: center;

    &__item {
      height: 40px;
      line-height: 40px;
      font-size: 13px;
    }
  }

  &-container {
    position: relative;
    overflow: hidden;

    @mixin thame() {
      content: "";
      position: absolute;
      background-color: #dbdde3;
      height: 0.5px;
      left: calc(100% / 6 - 15px);
      right: calc(100% / 6 - 7px);
      box-sizing: border-box;
      pointer-events: none;
    }

    &::before {
      @include thame();
      top: 50%;
      margin-top: -20px;
    }

    &::after {
      @include thame();
      bottom: 50%;
      margin-bottom: -20px;
    }
  }
}
.scrollbar {
  height: 200px;
  overflow: hidden;

  &-wrap {
    height: 100%;
    overflow-y: auto;
    scrollbar-width: none; /* firefox */
    -ms-overflow-style: none; /* IE 10+ */
    &::-webkit-scrollbar {
      display: none;
    }
  }

  &-view {
    &::before,
    &::after {
      content: "";
      display: block;
      width: 100%;
      height: 80px;
      box-sizing: border-box;
    }
  }
}
.is {
  &-active {
    font-weight: 700;
  }

  &-link {
    color: #6e7074;
    cursor: pointer;
  }

  &-disabled {
    color: #989ba0 !important;
  }
}

.select-disabled {
  position: absolute;
  right: 0;
  top: 0;
  bottom: 0;
  left: 0;
  z-index: 100;
  cursor: not-allowed;
}
</style>
